package com.zlcx.tz.live.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.zlcx.tz.live.client.UserClient;
import com.zlcx.tz.live.constant.RedisKeyConst;
import com.zlcx.tz.live.constant.YN;
import com.zlcx.tz.live.constant.dic.CHECK_IMAGE_RATE;
import com.zlcx.tz.live.domain.DicDO;
import com.zlcx.tz.live.domain.LiveRoomInfo;
import com.zlcx.tz.live.domain.LiveRoomJoinRecord;
import com.zlcx.tz.live.domain.LiveRoomPublisherRecord;
import com.zlcx.tz.live.domain.backup.LiveRoomInfoBackUpDO;
import com.zlcx.tz.live.domain.backup.LiveRoomJoinRecordBackUpDO;
import com.zlcx.tz.live.domain.backup.LiveRoomPublisherRecordBackUpDO;
import com.zlcx.tz.live.enums.*;
import com.zlcx.tz.live.exception.BusinessException;
import com.zlcx.tz.live.exception.CheckParamException;
import com.zlcx.tz.live.repository.LiveRoomInfoRepository;
import com.zlcx.tz.live.repository.LiveRoomJoinRecordRepository;
import com.zlcx.tz.live.repository.LiveSwitchedRepository;
import com.zlcx.tz.live.repository.backup.LiveRoomInfoBackupRepository;
import com.zlcx.tz.live.repository.backup.LiveRoomJoinRecordBackupRepository;
import com.zlcx.tz.live.repository.backup.LiveRoomPublisherRecordBackupRepository;
import com.zlcx.tz.live.utils.DozerMapper;
import com.zlcx.tz.live.utils.UpdateUtil;
import com.zlcx.tz.live.vo.*;
import com.zlcx.tz.live.vo.page.PageResultVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.zlcx.tz.live.common.config.RedisConfig.CacheKeys.ROOM_USER_LIST;
import static com.zlcx.tz.live.constant.dic.CHECK_IMAGE_RATE.check_rate;

/**
 * @AUTHOR xhl
 * @CREATE 2019-04-18 15:41
 **/
@Slf4j
@Service
public class LiveRoomService {
    @Autowired
    LiveRoomInfoRepository liveRoomInfoRepository;
    @Autowired
    LiveRoomJoinRecordRepository liveRoomJoinRecordRepository;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    LiveRoomJoinRecordService liveRoomJoinRecordService;
    @Autowired
    LiveRoomPublisherService liveRoomPublisherService;
    @Autowired
    MessageService messageService;
    @Autowired
    UserService userService;

    @Autowired
    UserClient userClient;

    @Autowired
    LiveComplaintService complaintService;

    @Autowired
    AgoraService agoraService;

    @Autowired
    RecordingService recordingService;

    @Value("${shangde.recordSecMin}")
    private Long recordSecMin;

    @Value("${shangde.userDefaultPic}")
    private String userDefaultPic;

    @Value("${agora.isRecord}")
    private Boolean isRecord;
//
//    @Autowired
//    private ChannelTopic recordVideoTopic;
//
//    @Autowired
//    private ChannelTopic recordVideoFinishTopic;


    @Autowired
    private LiveRoomInfoBackupRepository liveRoomInfoBackupRepository;
    @Autowired
    private LiveRoomJoinRecordBackupRepository liveRoomJoinRecordBackupRepository;
    @Autowired
    private LiveRoomPublisherRecordBackupRepository liveRoomPublisherRecordBackupRepository;
    @Autowired
    private DicServiceImpl dicService;
    @Autowired
    private LiveSwitchedRepository liveSwitchedRepository;

    private Logger traceLoger = LoggerFactory.getLogger("traceloger");

    /**
     * 创建房间
     *
     * @param roomInfoVO
     */
    public RoomInfoVO insertRoomInfo(RoomInfoParamVO roomInfoVO) {
        checkRoomInfo(roomInfoVO);

        LiveRoomInfo liveRoomInfo = new LiveRoomInfo();
        Date now = new Date();
        DozerMapper.getInstance().map(roomInfoVO, liveRoomInfo);
        liveRoomInfo.setCreateTime(now);
        liveRoomInfo.setRoomState(RoomState.CLOSED.getCode());//创建的时候默认为解散状态等有回调是在更改状态
        liveRoomInfo.setRoomMicState(RoomMicStatus.MIC_ON.getCode());
        liveRoomInfo.setRoomJoinerMax(500);//房间最大容量500
        liveRoomInfo.setUpdateTime(now);

        liveRoomInfo = liveRoomInfoRepository.save(liveRoomInfo);

        traceLoger.info("【{}】创建了房间【{}】，未激活", roomInfoVO.getOwnerUserId(), liveRoomInfo.getId());

        RoomInfoVO roomInfoVO1 = new RoomInfoVO();
        DozerMapper.getInstance().map(liveRoomInfo, roomInfoVO1);
        roomInfoVO1.setChannelId(roomInfoVO1.getId().toString());
        roomInfoVO1.setUserRole(RoomUserRole.OWNER.getCode());
        roomInfoVO1.setOwnerStudyContent(roomInfoVO.getStudyContent());
        return roomInfoVO1;
    }

    private void checkRoomInfo(RoomInfoParamVO roomInfoVO) {
        if (roomInfoVO == null) {
            throw new CheckParamException("参数不能为空");
        }
        if (StringUtils.isEmpty(roomInfoVO.getRoomName())) {
            throw new CheckParamException("房间名称不能为空");
        }
        if (roomInfoVO.getRoomPublisherMax() == null ||
                RoomPublisherMaxNum.getRoomPublisherMaxNum(roomInfoVO.getRoomPublisherMax()) == null) {
            throw new CheckParamException("连麦最大人数不合法");
        }
        if (StringUtils.isEmpty(roomInfoVO.getRoomAvatar())) {
            throw new CheckParamException("房间图片不能为空");
        }
        if (roomInfoVO.getOwnerUserId() == null || roomInfoVO.getOwnerUserId() == 0) {
            throw new CheckParamException("创建者id不正确");
        }
    }

    /**
     * 修改房间信息
     *
     * @param roomInfoVO
     * @return
     */
    @Transactional(rollbackOn = Exception.class)
    public RoomInfoVO updateRoomInfo(RoomInfoParamVO roomInfoVO) {

        if (roomInfoVO.getId() == null || roomInfoVO.getId() == 0) {
            throw new CheckParamException("房间id不正确");
        }

        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomInfoVO.getId());
        if (Objects.isNull(liveRoomInfo)) {
            throw new CheckParamException("房间不存在或者已经关闭");
        }
        //只修改传了值的字段
        UpdateUtil.copyNonNullProperties(roomInfoVO, liveRoomInfo);

        liveRoomInfo.setUpdateTime(new Date());
        liveRoomInfo = liveRoomInfoRepository.save(liveRoomInfo);

        RoomInfoVO roomInfoVO1 = new RoomInfoVO();
        DozerMapper.getInstance().map(liveRoomInfo, roomInfoVO1);
        roomInfoVO1.setChannelId(roomInfoVO1.getId().toString());
        roomInfoVO1.setUserRole(RoomUserRole.OWNER.getCode());

        if (StringUtils.isNotEmpty(roomInfoVO.getStudyContent())) {
            LiveRoomPublisherRecord publishingRecord = liveRoomPublisherService.getCurrentPublishingRecord(liveRoomInfo.getId(), roomInfoVO.getOwnerUserId());
            publishingRecord.setStudyContent(roomInfoVO.getStudyContent());
            liveRoomPublisherService.save(publishingRecord);
        }
        String key = RedisKeyConst.getRoomInfoByIdKey(liveRoomInfo.getId().toString());
        redisTemplate.delete(key);
        return roomInfoVO1;
    }


    public RoomInfoVO createRoomCallBack(RoomInfoParamVO roomInfoVO, String os) throws Exception {
        if (roomInfoVO == null) {
            throw new CheckParamException("参数不能为空");
        }
        if (roomInfoVO.getId() == null || roomInfoVO.getId() == 0) {
            throw new CheckParamException("房间id不正确");
        }
        if (roomInfoVO.getOwnerUserId() == null || roomInfoVO.getOwnerUserId() == 0) {
            throw new CheckParamException("创建者id不正确");
        }
        LiveRoomInfo liveRoomInfo = liveRoomInfoRepository.findOne(roomInfoVO.getId());
        if (liveRoomInfo == null) {
            log.info("createRoomCallBack no data found,roomId={} ", roomInfoVO.getId());
            throw new BusinessException("房间已关闭");
        }
        UserVO userVO = userService.getUserInfo(roomInfoVO.getOwnerUserId());
        if (userVO == null) {
            throw new BusinessException("用户不存在");
        }
        List<LiveRoomJoinRecord> liveRoomJoinRecords = liveRoomJoinRecordRepository.findByUserIdAndJoinState(roomInfoVO.getOwnerUserId(), RoomJoinStatus.IN.getCode());
        if (CollectionUtils.isEmpty(liveRoomJoinRecords)) {
            liveRoomInfoRepository.updateRoomState(RoomState.LIVING.getCode(), new Date(), roomInfoVO.getId());
            liveRoomInfo.setRoomState(RoomState.LIVING.getCode());
            traceLoger.info("【{}】创建了房间【{}】，已经激活", roomInfoVO.getOwnerUserId(), liveRoomInfo.getId());
            //添加用户上加入房间数据
            liveRoomJoinRecordService.userJoinRoom(roomInfoVO.getOwnerUserId(), liveRoomInfo.getId(), RoomUserRole.OWNER.getCode());
            //添加房主上麦信息
            liveRoomPublisherService.hostApplyPublish(roomInfoVO.getId(), roomInfoVO.getOwnerUserId(), roomInfoVO.getStudyContent());
//            recordingService.startRecording(roomInfoVO.getId());
            //TODO 删除房间列表缓存
//            delRoomListCache();
            messageService.joinRoom(userVO, liveRoomInfo, false);
            //创建房间之后通知其道友
            messageService.createRoomMsg(liveRoomInfo, roomInfoVO.getOwnerUserId());
        } else {
            LiveRoomJoinRecord joinRecord = liveRoomJoinRecords.get(0);
            if (joinRecord.getRoomId().longValue() != roomInfoVO.getId().longValue()) {
                traceLoger.info("【{}】创建了房间【{}】回调已经在其他房间【{}】", roomInfoVO.getOwnerUserId(), liveRoomInfo.getId(), roomInfoVO.getId());
                throw new BusinessException("已经在其他房间中");
            }
        }

        RoomInfoVO infoVO = new RoomInfoVO();
        DozerMapper.getInstance().map(liveRoomInfo, infoVO);
        infoVO.setUserRole(RoomUserRole.OWNER.getCode());
        infoVO.setOwnerStudyContent(roomInfoVO.getStudyContent());

        return processRoomInfoVO(infoVO, false, roomInfoVO.getOwnerUserId(), os);
    }


    /**
     * 根据id获取房间信息
     *
     * @param roomId
     * @return
     */
    public LiveRoomInfo getRoomInfoById(Long roomId) {
        if (roomId == null || roomId == 0) {
            throw new CheckParamException("房间id不正确");
        }

        String key = RedisKeyConst.getRoomInfoByIdKey(roomId.toString());
        try {
            LiveRoomInfo liveRoomInfo = (LiveRoomInfo) redisTemplate.opsForValue().get(key);
            if (liveRoomInfo != null) {
                return liveRoomInfo;
            }
        } catch (Exception e) {
            log.error("selectByChannelId get redis cache error={}", e);
        }
        LiveRoomInfo liveRoomInfo = liveRoomInfoRepository.findFirstByIdAndRoomStateIn(
                roomId, Arrays.asList(RoomState.LIVING.getCode(), RoomState.FULL.getCode()));
        if (liveRoomInfo == null) {
            return null;
        }
        try {
            redisTemplate.opsForValue().set(key, liveRoomInfo, 1, TimeUnit.MINUTES);
            return liveRoomInfo;
        } catch (Exception e) {
            log.error("selectByChannelId set redis cache error={}", e);
        }
        return null;
    }

    public LiveRoomInfo getRoomInfoNoState(Long roomId) {
        LiveRoomInfo roomInfo = getRoomInfoById(roomId);
        if (roomInfo != null) {
            return roomInfo;
        }

        String key = RedisKeyConst.getRoomInfoClosedByIdKey(roomId);
        try {
            roomInfo = (LiveRoomInfo) redisTemplate.opsForValue().get(key);
            if (roomInfo != null) {
                return roomInfo;
            }
        } catch (Exception e) {
            log.error("getRoomInfoNoState get redis cache error={}", e);
        }

        roomInfo = liveRoomInfoRepository.findOne(roomId);
        if (roomInfo != null) {
            try {
                redisTemplate.opsForValue().set(key, roomInfo, 1, TimeUnit.HOURS);
            } catch (Exception e) {
                log.error("selectByChannelId set redis cache error={}", e);
            }
        }

        return roomInfo;
    }

    /**
     * 快速加入
     *
     * @return
     */
    public Long quickJoinRoom(Long currentUserId) throws Exception {
        Long result = 0L;
        if (Objects.isNull(currentUserId)) {
            LiveRoomInfo liveRoomInfo = liveRoomInfoRepository.findFirstByRoomStateInOrderByCreateTimeDescRoomStateAsc(
                    Arrays.asList(RoomState.LIVING.getCode(), RoomState.FULL.getCode()));
            if (Objects.isNull(liveRoomInfo)) {
                return result;
            } else {
                return liveRoomInfo.getId();
            }
        }
        //按照时间倒叙查询到正在进行的直播房间，未满优先
        List<LiveRoomInfo> liveRoomInfos = liveRoomInfoRepository.findByRoomStateInOrderByCreateTimeDescRoomStateAsc(
                Arrays.asList(RoomState.LIVING.getCode(), RoomState.FULL.getCode()));

        if (CollectionUtils.isEmpty(liveRoomInfos)) {
            return result;
        }
        List<UserBlackVO> userBlackVOS = userService.userBlackList(currentUserId);
        Set<Long> longSet = userBlackVOS.stream().map(userBlackVO -> userBlackVO.getFriendsUserId()).collect(Collectors.toSet());
        for (LiveRoomInfo liveRoomInfo : liveRoomInfos) {
            Long roomId = liveRoomInfo.getId();
            //查询房主
            LiveRoomJoinRecord joinRecord = liveRoomJoinRecordService.getHostUserJoinRecord(roomId);
            //校验是否黑名单
            if (!longSet.contains(joinRecord.getUserId())) {
                result = roomId;
                break;
            }
        }
        return result;
    }

    /**
     * 加入房间
     *
     * @param userId
     * @param roomId
     * @return
     */
    public RoomInfoVO joinRoom(Long userId, Long roomId, boolean isVisitor, boolean sendMsg, boolean checkAudienceJoin, String os) throws Exception {
        traceLoger.info("joinRoom 用户【{}】进入房间【{}】，是否游客【{}】，是否校验观众及游客加入状态【{}】", userId, roomId, isVisitor, checkAudienceJoin);
        if (userId == null) {
            throw new CheckParamException("用户id不能为空");
        }
        if (roomId == null) {
            throw new CheckParamException("房间id不能为空");
        }
        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomId);
        if (liveRoomInfo == null) {
            traceLoger.info("joinRoom 用户【{}】进入房间【{}】，是否游客【{}】，房间不存在或者已经关闭", userId, roomId, isVisitor);
            throw new BusinessException("房间已关闭");
        }
        UserVO userVO = null;
        if (!isVisitor) {
            userVO = userService.getUserInfo(userId);
            if (userVO == null) {
                traceLoger.info("joinRoom 用户【{}】进入房间【{}】，是否游客【{}】，用户不存在", userId, roomId, isVisitor);
                throw new BusinessException("用户不存在");
            }
        } else {
            userVO = new UserVO();
            userVO.setId(userId);
            userVO.setName("游客");
            userVO.setAvatarUrl(userDefaultPic);
        }

        Integer beforeJoinStatus = YN.NO.getCode();
        Integer userRole = isVisitor ? RoomUserRole.VISITOR.getCode() : RoomUserRole.AUDIENCE.getCode();
        List<LiveRoomJoinRecord> liveRoomJoinRecords = liveRoomJoinRecordRepository.findByUserIdAndJoinState(userId, RoomJoinStatus.IN.getCode());
        if (CollectionUtils.isEmpty(liveRoomJoinRecords)) {
            liveRoomJoinRecordService.userJoinRoom(userId, roomId, userRole);
            // 通知进房间
            if (sendMsg) {
                messageService.joinRoom(userVO, liveRoomInfo, isVisitor);
            }
        } else {

            beforeJoinStatus = YN.YES.getCode();
            LiveRoomJoinRecord joinRecord = liveRoomJoinRecords.get(0);
            if (joinRecord.getRoomId().longValue() != roomId.longValue()) {
                traceLoger.info("joinRoom 用户【{}】进入房间【{}】，是否游客【{}】，已经在其他房间【{}】", userId, roomId, isVisitor, joinRecord.getRoomId());
                throw new BusinessException("已经在其他房间中");
            }
            userRole = joinRecord.getUserRole();

        }

        RoomInfoVO roomInfoVO = new RoomInfoVO();
        DozerMapper.getInstance().map(liveRoomInfo, roomInfoVO);
        roomInfoVO.setBeforeJoinStatus(beforeJoinStatus);
        roomInfoVO.setUserRole(userRole);
        return processRoomInfoVO(roomInfoVO, false, userId, "");
    }


    /**
     * 查询房间信息
     *
     * @param roomId
     * @return
     */
    public RoomInfoVO roomInfoVO(Long roomId, Long userId, boolean isVisitor, String os) {
        if (roomId == null) {
            throw new CheckParamException("roomId 不能为空");
        }
        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomId);
        if (liveRoomInfo == null) {
            throw new BusinessException("房间已关闭");
        }
        if (!isVisitor && userId != null) {
            UserVO userVO = userService.getUserInfo(userId);
            if (userVO == null) {
                throw new BusinessException("用户不存在");
            }
        }
        RoomInfoVO roomInfoVO = new RoomInfoVO();
        DozerMapper.getInstance().map(liveRoomInfo, roomInfoVO);
        log.debug(" roomInfoVO = {} liveRoomInfo = {}", roomInfoVO, liveRoomInfo);

        LiveRoomJoinRecord joinRecord = liveRoomJoinRecordService.getHostUserJoinRecord(roomId);
        LiveRoomPublisherRecord publishingRecord = liveRoomPublisherService.getCurrentPublishingRecord(liveRoomInfo.getId(), joinRecord.getUserId());
        roomInfoVO.setOwnerStudyContent(Objects.nonNull(publishingRecord) && Objects.nonNull(publishingRecord.getStudyContent()) ?
                publishingRecord.getStudyContent() : "连麦学习");
        return processRoomInfoVO(roomInfoVO, false, userId, os);
    }

    public RoomInfoSimpleVO roomInfoSimple(Long roomId) {
        if (roomId == null || roomId == 0) {
            throw new CheckParamException("roomId 不能为空");
        }
        LiveRoomInfo liveRoomInfo = getRoomInfoNoState(roomId);
        if (liveRoomInfo == null) {
            throw new BusinessException("房间不存在");
        }
        RoomInfoSimpleVO roomInfoVO = new RoomInfoSimpleVO();
        DozerMapper.getInstance().map(liveRoomInfo, roomInfoVO);
        return roomInfoVO;
    }


    /**
     * 装载房间信息
     *
     * @param roomInfoVO
     * @return
     */
    public RoomInfoVO processRoomInfoVO(RoomInfoVO roomInfoVO, boolean isNotListData, Long userId, String os) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("AudienceNumber");
        Long roomId = roomInfoVO.getId();
        roomInfoVO.setAudienceNumber(liveRoomJoinRecordService.getAudienceCount(roomId));
        stopWatch.stop();
        /*原本希望再查询列表得时候可以经过判断少查询一点数据
        if(!isNotListData){
        }*/
        stopWatch.start("findUserListByRoomId AUDIENCE");
        roomInfoVO.setAudiences(findUserListByRoomId(roomId, Lists.newArrayList(RoomUserRole.AUDIENCE.getCode(), RoomUserRole.VISITOR.getCode())));

        if (roomInfoVO.getAudiences() == null) {
            roomInfoVO.setAudiences(Lists.newArrayList());
        }
        if (roomInfoVO.getAudienceNumber() != roomInfoVO.getAudiences().size()) {
            roomInfoVO.setAudienceNumber(roomInfoVO.getAudiences().size());
            redisTemplate.delete(RedisKeyConst.getRoomAudienceCountKey(roomId.toString()));
        }
        stopWatch.stop();

        stopWatch.start("findUserListByRoomId publish");
        //TODO　查询连麦记录表，获取同意上麦时间，得出实时连麦时长


        roomInfoVO.setPublishers(findUserListByRoomId(
                roomId, Arrays.asList(RoomUserRole.OWNER.getCode(), RoomUserRole.PUBLISHER.getCode())));
        roomInfoVO.setPublisherNumber((int) liveRoomPublisherService.getPublishingCount(roomId));
        if (roomInfoVO.getPublishers() == null) {
            roomInfoVO.setPublishers(Lists.newArrayList());
        }
        if (roomInfoVO.getPublisherNumber() != roomInfoVO.getPublishers().size()) {
            roomInfoVO.setPublisherNumber(roomInfoVO.getPublishers().size());
            redisTemplate.delete(RedisKeyConst.getRoomPublisherCountKey(roomId.toString()));
        }
        stopWatch.stop();

        roomInfoVO.setChannelId(roomInfoVO.getId().toString());
        stopWatch.start("getHostUserJoinRecord");
        LiveRoomJoinRecord roomJoinRecord = liveRoomJoinRecordService.getHostUserJoinRecord(roomId);
        stopWatch.stop();
        stopWatch.start("end");
        roomInfoVO.setOwnerUserId(roomJoinRecord.getUserId());
        if (roomInfoVO.getUserRole() == null && userId != null) {
            if (userId.equals(roomInfoVO.getOwnerUserId())) {
                roomInfoVO.setUserRole(RoomUserRole.OWNER.getCode());
                LiveRoomPublisherRecord record = liveRoomPublisherService.getCurrentPublishingRecord(roomId, userId);
                if (Objects.nonNull(record)) {
                    roomInfoVO.setOwnerStudyContent(record.getStudyContent());
                }
            } else {
                if (CollectionUtils.isNotEmpty(roomInfoVO.getPublishers())
                        && roomInfoVO.getPublishers().stream().anyMatch(joinVO -> joinVO.getUserId().equals(userId))) {
                    roomInfoVO.setUserRole(RoomUserRole.PUBLISHER.getCode());
                } else if (CollectionUtils.isNotEmpty(roomInfoVO.getAudiences())
                        && roomInfoVO.getAudiences().stream().anyMatch(joinVO -> joinVO.getUserId().equals(userId) && joinVO.getUserRole() == RoomUserRole.VISITOR.getCode())) {
                    roomInfoVO.setUserRole(RoomUserRole.VISITOR.getCode());
                } else {
                    roomInfoVO.setUserRole(RoomUserRole.AUDIENCE.getCode());
                }
            }
        }
        DicDO checkRateDO = dicService.findByCode(CHECK_IMAGE_RATE.dicName());
        int checkRate = checkRateDO.filter(check_rate.name()).getCode().intValue();
        roomInfoVO.setCheckRate(checkRate);
        if ("Android".equals(os)) {
            roomInfoVO.setCheckRate(checkRate * 1000);
        }
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());

        if (StringUtils.isBlank(roomInfoVO.getRoomDesc())) {
            roomInfoVO.setRoomDesc("连麦学习");
        }

        return roomInfoVO;
    }

    public List<UserJoinVO> findUserListByRoomId(Long roomId, List<Integer> roleType) {
        Long now = System.currentTimeMillis();
        return liveRoomJoinRecordService.findUsersByRoomId(roomId)
                .stream().filter(vo -> roleType.contains(vo.getUserRole())).map(userJoinVO -> {
                    Date agreeTime = liveRoomPublisherService.getStartPublishingDate(userJoinVO.getUserId(), roomId);
                    if (Objects.nonNull(agreeTime)) {
                        userJoinVO.setLiveTimes((now - agreeTime.getTime()) / 1000);
                    }
                    return userJoinVO;
                })
                .collect(Collectors.toList());
    }

    public Page<LiveRoomInfo> findAllClosedRoom(Pageable pageable) {
        //获取一小个小时前数据
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR,
                calendar.get(Calendar.HOUR) - 1);

        Specification<LiveRoomInfo> specification = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(cb.greaterThanOrEqualTo(root.get("roomState"), RoomState.CLOSED.getCode()));
            list.add(cb.lessThanOrEqualTo(root.get("createTime"), calendar.getTime()));
            return cb.and(list.toArray(new Predicate[list.size()]));
        };
        return liveRoomInfoRepository.findAll(specification, pageable);
    }

    /**
     * 一小时以前窗前的房间
     */
    public static final int CLOSE_ROOM_MINUTE = 60 * 8;

    public Page<LiveRoomInfo> find1HourOpenRoom(Pageable pageable) {
        //获取一小个小时前数据
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.MINUTE,
                calendar.get(Calendar.MINUTE) - CLOSE_ROOM_MINUTE);

        Specification<LiveRoomInfo> specification = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(cb.lessThanOrEqualTo(root.get("roomState"), RoomState.FULL.getCode()));
            list.add(cb.lessThanOrEqualTo(root.get("createTime"), calendar.getTime()));
            return cb.and(list.toArray(new Predicate[list.size()]));
        };
        return liveRoomInfoRepository.findAll(specification, pageable);
    }

    @Transactional(rollbackOn = Exception.class)
    public void backup(LiveRoomInfo liveRoomInfo) {
        List<LiveRoomJoinRecord> joinRecordList = liveRoomJoinRecordRepository.findByRoomId(liveRoomInfo.getId());
        List<LiveRoomPublisherRecord> publisherRecordList = liveRoomPublisherService.findByRoomId(liveRoomInfo.getId());

        liveRoomInfoBackupRepository.save(LiveRoomInfoBackUpDO.from(liveRoomInfo));
        liveRoomInfoRepository.delete(liveRoomInfo.getId());
        joinRecordList.forEach(joinRecord ->
        {
            liveRoomJoinRecordBackupRepository.save(LiveRoomJoinRecordBackUpDO.from(joinRecord));
            liveRoomJoinRecordRepository.delete(joinRecord.getId());

        });
        publisherRecordList.forEach(publisherRecord -> {
            liveRoomPublisherRecordBackupRepository.save(LiveRoomPublisherRecordBackUpDO.from(publisherRecord));
            liveRoomPublisherService.delete(publisherRecord.getId());
        });
    }

    /**
     * 获取房间列表
     *
     * @param page
     * @param pageSize
     * @return
     */
    public PageResultVO findRoomList(Long userId, Integer page, Integer pageSize) throws Exception {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("findList");
        Map roomListMap = getRoomInfoList(page, pageSize, null);
        stopWatch.stop();
        List<RoomInfoVO> roomInfoVOS = (List<RoomInfoVO>) roomListMap.get("roomList");
        Long count = (Long) roomListMap.get("count");
        if (CollectionUtils.isEmpty(roomInfoVOS) || count == null) {
            return new PageResultVO();
        } else {
            if (userId != null && userId != 0L) {//过滤用户黑名单创建得房间
                stopWatch.start("filterUserBlackList");
                roomInfoVOS = filterUserBlackList(roomInfoVOS, userId);
                stopWatch.stop();
            }
            if (CollectionUtils.isEmpty(roomInfoVOS)) {
                return new PageResultVO();
            }
        }
        stopWatch.start("processRoomInfoVO");
        roomInfoVOS.stream().forEach(vo -> {
            try {
                vo = processRoomInfoVO(vo, true, null, "");
            } catch (Exception e) {
                log.error("processRoomInfoVO error={}", e);
            }
        });
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
        return new PageResultVO(count, roomInfoVOS);

    }

    /**
     * 过滤用户黑名单创建得房间
     *
     * @param roomInfoVOS
     * @param userId
     * @return
     */
    private List<RoomInfoVO> filterUserBlackList(List<RoomInfoVO> roomInfoVOS, Long userId) {
        List<UserBlackVO> userBlackVOS = userService.userBlackList(userId);
        if (CollectionUtils.isEmpty(userBlackVOS)) {
            return roomInfoVOS;
        }
        List<Long> blackIds = userBlackVOS.stream().map(userBlackVO -> userBlackVO.getFriendsUserId()).collect(Collectors.toList());
        List<RoomInfoVO> list = new ArrayList<>();
        for (int i = 0; i < roomInfoVOS.size(); i++) {
            try {
                List<UserJoinVO> userJoinVOS = findUserListByRoomId(
                        roomInfoVOS.get(i).getId(), Lists.newArrayList(RoomUserRole.OWNER.getCode()));
                if (CollectionUtils.isNotEmpty(userJoinVOS) && blackIds.contains(userJoinVOS.get(0).getUserId())) {
                    continue;
                }
            } catch (Exception e) {
                log.error("filterUserBlackList :findUserListByRoomId error={}", e);
            }
            list.add(roomInfoVOS.get(i));

        }
        return list;
    }

    /**
     * 获取装载数据前的房间数据
     *
     * @return
     */
    private Map<String, Object> getRoomInfoList(Integer page, Integer pageSize, String createTime) throws Exception {
        //按创建房间的时间 倒序排序，若时间相同，就上麦有空缺的优先排在最上方
//        String key = RedisKeyConst.getRoomListKey();
//        String countKey = RedisKeyConst.getRoomListCountKey();
//        Long count = 0L;
//        List<RoomInfoVO> roomInfoVOS = new ArrayList<>();
        Map roomListMap = new HashMap<String, Object>();
//        try {
//            roomInfoVOS = (List<RoomInfoVO>) redisTemplate.opsForValue().get(key);
//            Object c = redisTemplate.opsForValue().get(countKey);
//            if (c != null) {
//                count = Long.parseLong(c.toString());
//            }
//        } catch (Exception e) {
//            log.error("findRoomList get redis cache error ={}", e);
//        }
        List<RoomInfoVO> roomInfoVOList = new ArrayList<>();
        //先不进行分页查询，先将所有有效的连麦数据查询出来放到缓存中然后分页
        //客户端业务逻辑为了防止页面出现重复数据，默认传回页面数据上最小的createTime，
        //服务端根据createTime传回小于此createTime的最近10条
        if (StringUtils.isNotEmpty(createTime)) {
            page = 0;
        }
        Pageable pageable = new PageRequest(page, pageSize, Sort.Direction.DESC, "createTime");
        Page<LiveRoomInfo> pages = liveRoomInfoRepository.findAll(new Specification<LiveRoomInfo>() {
            @Override
            public Predicate toPredicate(Root<LiveRoomInfo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                CriteriaBuilder.In<Integer> typeIn = criteriaBuilder.in(root.get("roomState"));
                typeIn.value(RoomState.LIVING.getCode()).value(RoomState.FULL.getCode());
                predicates.add(typeIn);
                if (StringUtils.isNotEmpty(createTime)) {
                    SimpleDateFormat dtf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    try {
                        predicates.add(criteriaBuilder.lessThan(root.get("createTime"), dtf2.parse(createTime)));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        }, pageable);
        List<LiveRoomInfo> liveRoomInfos = pages.getContent();
        List<RoomInfoVO> roomInfoVOS = new ArrayList<>();
        DozerMapper.copyPropertiesList(liveRoomInfos, roomInfoVOS, RoomInfoVO.class);

        roomInfoVOS.stream().forEach(vo -> {
            List<UserJoinVO> liveRoomJoinRecords = new ArrayList<>();
            try {
                liveRoomJoinRecords = findUserListByRoomId(vo.getId(), Arrays.asList(RoomUserRole.OWNER.getCode(), RoomUserRole.PUBLISHER.getCode()));
                //findUserListByRoomId(vo.getId(), Arrays.asList(RoomUserRole.OWNER.getCode()));
            } catch (Exception e) {
                log.error("getHomePageList findUserListByRoomId error={}", e);
            }
            if (!CollectionUtils.isEmpty(liveRoomJoinRecords)) {
                vo.setPublishers(liveRoomJoinRecords);
                vo.setOwnerUserId(liveRoomJoinRecords.stream().filter(userJoinVO -> userJoinVO.getUserRole().intValue() == RoomUserRole.OWNER.getCode()).findFirst().get().getUserId());
            }
        });
        roomInfoVOList = roomInfoVOS.stream().filter(roomInfoVO -> Objects.nonNull(roomInfoVO.getOwnerUserId())).collect(Collectors.toList());
        roomListMap.put("roomList", roomInfoVOList);
        roomListMap.put("count", pages.getTotalElements());
        return roomListMap;
    }

    /**
     * 退出房间
     *
     * @param userId
     * @param roomId
     */
    public void exitRoom(Long userId, Long roomId, boolean isVisitor, boolean sendMsg) {
        traceLoger.info("exitRoom 用户【{}】退出房间【{}】，是否游客【{}】，是否发消息【{}】", userId, roomId, isVisitor, sendMsg);
        if (userId == null || roomId == null) {
            throw new CheckParamException("参数不合法");
        }
        LiveRoomInfo liveRoomInfo = getRoomInfoNoState(roomId);
        if (liveRoomInfo == null) {
            return;
        }
        UserVO userVO = null;
        if (!isVisitor) {
            userVO = userService.getUserInfo(userId);
            if (userVO == null) {
                traceLoger.info("exitRoom 用户【{}】退出房间【{}】，是否游客【{}】，是否发消息【{}】，用户不存在", userId, roomId, isVisitor, sendMsg);
//                throw new BusinessException("用户不存在");
            }
        } else {
            userVO = new UserVO();
            userVO.setId(userId);
            userVO.setName("游客");
            userVO.setAvatarUrl(userDefaultPic);
        }

        boolean exit = liveRoomJoinRecordService.exitRoom(roomId, userId, RoomJoinStatus.OUT.getCode(), RoomState.CLOSED.getCode());
        if (!isVisitor) {
            startCreateVideo(userId, roomId);
        }

        if (sendMsg && exit) {
            //调消息系统发布用户退出房间消息
            messageService.exitRoom(userVO, liveRoomInfo, isVisitor);
        }

    }

    /**
     * 将用户踢出房间
     *
     * @param ownerUserId
     * @param targetUserId
     * @param roomId
     */
    public void kickingUser(Long ownerUserId, Long targetUserId, Long roomId) throws Exception {
        traceLoger.info("kickingUser房间【{}】房主【{}】将 用户【{}】踢出房间", roomId, ownerUserId, targetUserId);
        if (ownerUserId == null) {
            throw new CheckParamException("房主id不能为空");
        }
        if (targetUserId == null) {
            throw new CheckParamException("目标用户id不能为空");
        }
        if (roomId == null) {
            throw new CheckParamException("房间id不能为空");
        }
        if (!liveRoomJoinRecordService.verifyOwner(roomId, ownerUserId)) {
            traceLoger.info("kickingUser房间【{}】房主【{}】将 用户【{}】踢出房间，不是房主", roomId, ownerUserId, targetUserId);
            throw new BusinessException("用户没有权限");
        }
        if (ownerUserId.equals(targetUserId)) {
            traceLoger.info("kickingUser房间【{}】房主【{}】将 用户【{}】踢出房间，不是此房间房主本人", roomId, ownerUserId, targetUserId);
            throw new CheckParamException("非法操作");
        }

        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomId);
        if (liveRoomInfo == null) {
            traceLoger.info("kickingUser房间【{}】房主【{}】将 用户【{}】踢出房间，房间已经关闭", roomId, ownerUserId, targetUserId);
            throw new BusinessException("房间已关闭");
        }

        UserJoinVO userJoinVO = liveRoomJoinRecordService.getUserJoinByRoomIdAndUserId(roomId, targetUserId);
        if (userJoinVO == null) {
            return;
        }

        liveRoomJoinRecordService.exitRoom(roomId, targetUserId, RoomJoinStatus.KICKED.getCode(), RoomState.CLOSED.getCode());
        //调消息系统，通知被踢用户退出房间，通知其他用户该用户被踢消息
        UserVO userVO = liveRoomJoinRecordService.getFillUserInfo(targetUserId, userJoinVO.getUserRole() == RoomUserRole.VISITOR.getCode());
        messageService.kicking(ownerUserId, userVO, liveRoomInfo, "user");
        messageService.exitRoom(userVO, liveRoomInfo, false);
//        agoraService.createKickingRule(roomId, targetUserId, null, 1);
    }

    /**
     * 管理员踢人
     *
     * @param userId
     * @throws Exception
     */
    public void adminKickingUser(Long userId, String role) {
        traceLoger.info("adminKickingUser 管理员将 用户【{}】踢出房间，角色【{}】", userId, role);
        if (userId == null) {
            throw new CheckParamException("用户id不能为空");
        }

        LiveRoomJoinRecord roomJoinRecord = liveRoomJoinRecordService.getUserJoinRecord(userId);
        if (roomJoinRecord == null) {
            return;
        }

        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomJoinRecord.getRoomId());
        if (liveRoomInfo == null) {
            traceLoger.info("adminKickingUser 管理员将 用户【{}】踢出房间，角色【{}】，房间已经关闭", userId, role);
            throw new BusinessException("房间已关闭");
        }
        UserVO userVO = liveRoomJoinRecordService.getFillUserInfo(userId, roomJoinRecord.getUserRole() == RoomUserRole.VISITOR.getCode());
        messageService.kicking(userId, userVO, liveRoomInfo, role);
        messageService.exitRoom(userVO, liveRoomInfo, false);
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        liveRoomJoinRecordService.exitRoom(liveRoomInfo.getId(), userId, RoomJoinStatus.KICKED.getCode(), RoomState.CLOSED.getCode());
//        agoraService.createKickingRule(liveRoomInfo.getId(), userId, null, 1);
    }


    /**
     * 房主关闭房间
     *
     * @param userId
     * @param roomId
     */
    public void roomOwnerClosedRoom(Long userId, Long roomId) throws Exception {
        traceLoger.info("roomOwnerClosedRoom 房主【{}】关闭房间【{}】", userId, roomId);
        if (liveRoomJoinRecordService.verifyOwner(roomId, userId)) {
            startCreateVideo(userId, roomId);
            closeRoom(roomId, RoomState.CLOSED.getCode(), userId);
        } else {
            traceLoger.info("roomOwnerClosedRoom 房主【{}】关闭房间【{}】,不是房主", userId, roomId);
        }
    }


    /**
     * 系统1小时自动关闭房间
     *
     * @param userId
     * @param roomId
     */
    public void autoClosedRoom(Long userId, Long roomId) {
        traceLoger.info("roomOwnerClosedRoom 房主【{}】关闭房间【{}】", userId, roomId);
        if (liveRoomJoinRecordService.verifyOwner(roomId, userId)) {
            startCreateVideo(userId, roomId);
            autoCloseRoom(roomId, RoomState.CLOSED.getCode(), userId);
        } else {
            traceLoger.info("roomOwnerClosedRoom 房主【{}】关闭房间【{}】,不是房主", userId, roomId);
        }
    }


    private void startCreateVideo(Long userId, Long roomId) {

        if (!isRecord) {
            return;
        }

        Long studySeconds = studySeconds(userId, roomId);
        if (studySeconds >= recordSecMin) {
            RecordVideoMessage recordVideoMessage = new RecordVideoMessage();
            recordVideoMessage.setRoomId(roomId);
            recordVideoMessage.setUserId(userId);
            recordVideoMessage.setStudySeconds(studySeconds);
            recordVideoMessage.setRecordingDir(getRecordPicDir(userId, roomId));
            recordVideoMessage.setStartPublishingDate(liveRoomPublisherService.getStartPublishingDate(userId, roomId));
//            redisTemplate.convertAndSend(recordVideoTopic.getTopic(), JSON.toJSONString(recordVideoMessage));

            String createVideoStatusKey = RedisKeyConst.getCreateVideoStatusKey(userId, roomId);
            redisTemplate.opsForValue().set(createVideoStatusKey, 1, 10, TimeUnit.MINUTES);
        } else {
            redisTemplate.delete(RedisKeyConst.getRecordingPicDirKey(userId, roomId));
        }

    }

    private Long studySeconds(Long userId, Long roomId) {
        return liveRoomPublisherService.studySeconds(userId, roomId);
    }

    private String getRecordPicDir(Long userId, Long roomId) {
        String recordingPicDirKey = RedisKeyConst.getRecordingPicDirKey(userId, roomId);
        String recordingPicDir = (String) redisTemplate.opsForValue().get(recordingPicDirKey);
        return recordingPicDir;
    }

    /**
     * 管理员关闭房间
     *
     * @param roomId
     */
    public void administratorCloseRoom(Long roomId) throws Exception {
        traceLoger.info("administratorCloseRoom 管理员关闭房间【{}】", roomId);

        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomId);
        if (liveRoomInfo == null) {
            traceLoger.info("administratorCloseRoom 管理员关闭房间【{}】，房间已经关闭", roomId);
            throw new BusinessException("房间已经关闭！");
        }

        LiveRoomJoinRecord userJoinVO = liveRoomJoinRecordService.getHostUserJoinRecord(roomId);
        closeRoom(roomId, RoomState.BE_CLOSED.getCode(), userJoinVO.getUserId());
        complaintService.dealComplaint(roomId);
    }

    /**
     * 关闭房间
     *
     * @param roomId
     */
    public void closeRoom(Long roomId, Integer closeState, Long userId) throws Exception {
        traceLoger.info("closeRoom 用户【{}】关闭房间【{}】，关闭状态【{}】", userId, roomId, RoomState.getRoomState(closeState).getTips());
        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomId);
        if (liveRoomInfo == null) {
            return;
        }

        /**
         * 先关房间，否则极端情况下查询会报空
         */
        //修改房间状态
        updateRoomState(roomId, closeState);
        //将所有连麦者下麦
        liveRoomPublisherService.closeRoomQuitPublish(roomId);
        //修改所用房间内用户的状态并删除缓存
        liveRoomJoinRecordService.closeRoomUpdateJoinRecordState(roomId, closeState);

        // 通过消息系统通知所有用户退出房间
        messageService.closeRoom(liveRoomInfo, userId, closeState);
        agoraService.createKickingRule(roomId, null, null, 60);
    }

    /**
     * 1小时自动关闭房间
     *
     * @param roomId
     */
    public void autoCloseRoom(Long roomId, Integer closeState, Long userId) {
        traceLoger.info("autoCloseRoom 用户【{}】关闭房间【{}】，关闭状态【{}】", userId, roomId, RoomState.getRoomState(closeState).getTips());
        LiveRoomInfo liveRoomInfo = getRoomInfoById(roomId);
        if (liveRoomInfo == null) {
            return;
        }
        /**
         * 先关房间，否则极端情况下查询会报空
         */
        //修改房间状态
        updateRoomState(roomId, closeState);
        //将所有连麦者下麦
        liveRoomPublisherService.closeRoomQuitPublish(roomId);
        //修改所用房间内用户的状态并删除缓存
        liveRoomJoinRecordService.closeRoomUpdateJoinRecordState(roomId, closeState);

        // 通过消息系统通知所有用户退出房间
        messageService.closeRoom(liveRoomInfo, userId, 5);
        agoraService.createKickingRule(roomId, null, null, 60);
    }


    //TODO 去掉房间缓存列表
//    private void delRoomListCache() {
//        //删除房间列表缓存
//        try {
//
//            redisTemplate.execute((RedisCallback) connection -> {
//                long n = 0;
//                Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match("tz-live-video:room:list*").count(10000).build());
//                while (cursor.hasNext()) {
//                    byte[] bytes = cursor.next();
//                    connection.del(bytes);
//                    n++;
//                }
//                return Long.valueOf(n);
//            });
//            String countKey = RedisKeyConst.getRoomListCountKey();
//            redisTemplate.delete(countKey);
//
//        } catch (Exception e) {
//            log.error("insertRoomInfo delete roomList cache error={}", e);
//        }
//    }

    /**
     * 修改房间状态
     *
     * @param roomId
     * @param roomState
     */
    public void updateRoomState(Long roomId, Integer roomState) {
        liveRoomInfoRepository.updateRoomState(roomState, new Date(), roomId);
        String key = RedisKeyConst.getRoomInfoByIdKey(roomId.toString());
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("updateRoomState delete cache error ={}", e);
        }
        if (RoomState.CLOSED.getCode() == roomState) {
            String delayKey = RedisKeyConst.getRoomCloseDelayStatusKey(roomId);
            redisTemplate.opsForValue().set(delayKey, roomId, 50, TimeUnit.SECONDS);
        }
        //TODO 删除房间列表
        //delRoomListCache();
    }

    /**
     * 全员禁麦，开麦。
     *
     * @param roomId
     * @param micState
     */
    @Caching(evict = {
            @CacheEvict(cacheNames = ROOM_USER_LIST, key = "#roomId")
    })
    public void updateRoomMicState(Long roomId, Integer micState, Long hostUserId) throws Exception {
        Date now = new Date();
        liveRoomInfoRepository.updateRoomMicState(micState, now, roomId);

        if (RoomMicStatus.MIC_OFF.getCode() == micState) {
            liveRoomJoinRecordRepository.operateAllMicClose(now, roomId, hostUserId);
        } else if (RoomMicStatus.MIC_ON.getCode() == micState) {
            liveRoomJoinRecordRepository.operateAllMicOpen(now, roomId, hostUserId);
        } else {

        }

        String key = RedisKeyConst.getRoomInfoByIdKey(roomId.toString());
        try {
            redisTemplate.delete(key);
            //TODO 删除房间用户列表缓存
//            //删除房间用户列表数据
//            redisTemplate.execute((RedisCallback) connection -> {
//                long n = 0;
//                Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match("tz-live-video:room:usersList*").count(10000).build());
//                while (cursor.hasNext()) {
//                    byte[] bytes = cursor.next();
//                    connection.del(bytes);
//                    n++;
//                }
//                return Long.valueOf(n);
//            });

        } catch (Exception e) {
            log.error("updateRoomMicState delete cache error ={}", e);
        }
    }

    /**
     * 为首页提供的房间列表
     *
     * @param page
     * @param pageSize
     */
    public PageResultVO getHomePageList(Long userId, Integer page, Integer pageSize, String createTime) throws Exception {
        Map roomListMap = getRoomInfoList(page, pageSize, createTime);
        List<RoomInfoVO> roomInfoVOS = (List<RoomInfoVO>) roomListMap.get("roomList");
        Long count = (Long) roomListMap.get("count");
        if (CollectionUtils.isEmpty(roomInfoVOS) || count == null) {
            return new PageResultVO();
        } else {
            if (userId != null && userId != 0L) {//过滤用户黑名单创建得房间
                roomInfoVOS = filterUserBlackList(roomInfoVOS, userId);
            }
            if (CollectionUtils.isEmpty(roomInfoVOS)) {
                return new PageResultVO();
            }
            if (StringUtils.isNotEmpty(createTime)) {
                DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime localDateTime = LocalDateTime.parse(createTime, dtf2);
                Long milliSecond = localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
                roomInfoVOS = roomInfoVOS.stream().filter(roomInfoVO -> roomInfoVO.getCreateTime().getTime() < milliSecond).limit(pageSize).collect(Collectors.toList());
            }

            if (CollectionUtils.isEmpty(roomInfoVOS)) {
                return new PageResultVO();
            }
        }
        List<HomePageListVO> homePageListVOS = new ArrayList<>();
        DozerMapper.copyPropertiesList(roomInfoVOS, homePageListVOS, HomePageListVO.class);
        Long now = System.currentTimeMillis();
        homePageListVOS.stream().forEach(vo -> {
            vo.setPublisherNumber((int) liveRoomPublisherService.getPublishingCount(vo.getId()));
            vo.setAudienceNumber(liveRoomJoinRecordService.getAudienceCount(vo.getId()));
            vo.setRoomTimes((now - vo.getCreateTime().getTime()) / 1000);
        });
        return new PageResultVO(count, homePageListVOS);
    }

    /**
     * 根据房间id查询房间内用户id
     *
     * @param roomId
     * @param queryType
     * @return
     * @throws Exception
     */
    public List<Long> getUserIdsByRoomId(Long roomId, Integer queryType) throws Exception {
        if (getRoomInfoById(roomId) == null) {
            throw new CheckParamException("房间已关闭");
        }
        if (queryType == 1) {
            //查询连麦人数
            List<UserJoinVO> userJoinVOS = findUserListByRoomId(
                    roomId, Arrays.asList(RoomUserRole.OWNER.getCode(), RoomUserRole.PUBLISHER.getCode()));
            if (CollectionUtils.isNotEmpty(userJoinVOS)) {
                return userJoinVOS.stream().map(vo -> vo.getUserId()).collect(Collectors.toList());
            }
        } else if (queryType == 2) {
            //查询连麦人数
            List<UserJoinVO> userJoinVOS = findUserListByRoomId(
                    roomId, Arrays.asList(RoomUserRole.AUDIENCE.getCode(), RoomUserRole.VISITOR.getCode()));
            if (CollectionUtils.isNotEmpty(userJoinVOS)) {
                return userJoinVOS.stream().map(vo -> vo.getUserId()).collect(Collectors.toList());
            }
        } else {
            //查询连麦人数
            List<UserJoinVO> userJoinVOS = findUserListByRoomId(
                    roomId, Arrays.asList(RoomUserRole.OWNER.getCode(), RoomUserRole.PUBLISHER.getCode(), RoomUserRole.AUDIENCE.getCode(), RoomUserRole.VISITOR.getCode()));
            if (CollectionUtils.isNotEmpty(userJoinVOS)) {
                return userJoinVOS.stream().map(vo -> vo.getUserId()).collect(Collectors.toList());
            }
        }
        return new ArrayList<>();
    }

    public void closeUnnormalClosedRoom() {
        List<LiveRoomInfo> list = liveRoomInfoRepository.getUnnormalClosedRoom();
        if (!CollectionUtils.isEmpty(list)) {
            List<Long> ids = list.stream().map(LiveRoomInfo::getId).collect(Collectors.toList());
            traceLoger.info("closeUnnormalClosedRoom {}", JSON.toJSONString(ids));
            liveRoomInfoRepository.closeUnnormalClosedRoom(ids, new Date());
        }
    }

    public RoomPublisherVO findPublisherRecordByUserId(Long userId) {
        return liveRoomPublisherService.findPublisherRecordByUserId(userId);
    }

    /**
     * 切换房主更新房间头像，将房间描述改为默认值
     *
     * @param roomId
     * @param roomAvatar
     */

    public void updateRoomAvatarAndDesc(Long roomId, String roomAvatar) {
        liveRoomInfoRepository.updateRoomAvatarAndDesc(roomAvatar, "连麦学习", new Date(), roomId);
        String key = RedisKeyConst.getRoomInfoByIdKey(roomId.toString());
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("", e);
        }
    }

    public void updateRoomAvatar(Long roomId, String roomAvatar) {
        liveRoomInfoRepository.updateRoomAvatar(roomAvatar, new Date(), roomId);
        String key = RedisKeyConst.getRoomInfoByIdKey(roomId.toString());
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("", e);
        }
    }


    public List<Long> findAllOpenRoomIds() {
        return liveRoomInfoRepository.findAllOpenRoomIds();
    }
}
